package com.yenlien.traceyun.webase.controller;


import com.alibaba.fastjson.JSONObject;
import com.yenlien.traceyun.common.exception.TokenException;
import com.yenlien.traceyun.webase.exception.BusinessException;
import com.yenlien.traceyun.webase.exception.CommonException;
import com.yenlien.traceyun.webase.exception.ErrorPageException;
import com.yenlien.traceyun.webase.exception.RestException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author wangyang@3sqy.cn
 * @version V1.0
 * @date 2017年11月14日
 * @Description:统一异常处理
 */
@ControllerAdvice
public class GlobalExceptionHandler {


    private Logger loger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ResponseBody
    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public Object exceptionHandler(MethodArgumentNotValidException e) {
        Map<String, String> collect = e.getBindingResult().getFieldErrors().stream()
                .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
        return errorResponse("400", collect);
    }

    @ResponseBody
    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public Object exceptionHandler(BindException e) {
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        String rejectedValue = "";
        if (!fieldErrors.isEmpty()) {
            for (FieldError fieldError : fieldErrors) {
                rejectedValue = fieldError.getDefaultMessage();
            }
        }
        return errorResponse("400", rejectedValue);
    }

    @ResponseBody
    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public Object exceptionHandler(DataIntegrityViolationException e) {
        throw new BusinessException("字段不能重复");
    }


    /**
     * Exception 异常
     *
     * @param e execption
     * @return object
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public Object exceptionHandler(Exception e) {
        e.printStackTrace();
        log(e);
        return errorResponse("500", e.getMessage());
    }

    /**
     * BusinessException 异常
     *
     * @param e e
     * @return object
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public Object businessExceptionExceptionHandler(BusinessException e) {
        return errorResponse(e.getCode(), e.getMsg());
    }

    /**
     * BusinessException 异常
     *
     * @param e e
     * @return object
     */
    @ExceptionHandler(CommonException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public Object commonExceptionExceptionHandler(CommonException e) {
        return errorResponse(e.getCode(), e.getMsg());
    }

    /**
     * token 异常
     *
     * @param e e
     * @return object
     */
    @ExceptionHandler(TokenException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public Object tokenExceptionHandler(TokenException e) {
        return errorResponse("t001", e.getMessage());
    }

    /**
     * rest 异常
     *
     * @param e e
     * @return object
     */
    @ExceptionHandler(RestException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public Object restExceptionHandler(RestException e) {
        if (StringUtils.isEmpty(e.getCode())) {
            return errorResponse("500", e.getMessage());
        }
        return errorResponse(e.getCode(), e.getMessage());
    }


    /**
     * ErrorPageException 异常
     *
     * @param e e
     * @return object
     * @throws IOException
     */
    @ExceptionHandler(ErrorPageException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public void errorPageExceptionHandler(ErrorPageException e, HttpServletResponse rsp) throws IOException {
        rsp.setContentType("text/html; charset=utf-8");
        ;
        rsp.getOutputStream().write(e.getMessage().getBytes("UTF-8"));
    }

    /**
     * 构建返回
     *
     * @param code    code
     * @param message message
     * @return object
     */
    private JSONObject errorResponse(String code, Object message) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        JSONObject object = new JSONObject();
        object.put("code", code);
        object.put("errMsg", message);
//        object.put("url", request.getRequestURI().toString());
//        object.put("urlParam", request.getQueryString());
        return object;
    }

    /**
     * 构建返回
     *
     * @param String message
     * @return object
     */
    private JSONObject errorResponse(String message) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        JSONObject object = new JSONObject();
        object.put("code", "500");
        object.put("errMsg", message);
//        object.put("url", request.getRequestURI().toString());
//        object.put("urlParam", request.getQueryString());
        return object;
    }

    private void log(Throwable throwable) {
        if (throwable != null) {
            int length = throwable.getStackTrace().length;
            if (length > 0) {
                StackTraceElement element = throwable.getStackTrace()[0];
                String exceptionInfo = null;
                if (element == null)
                    return;
                exceptionInfo = "class=" + element.getClassName() + ",method=" + element.getMethodName() + ",file="
                        + element.getFileName() + ",line=" + element.getLineNumber();
                loger.error("error begin,exception:{},msg:{}", exceptionInfo, throwable.getMessage());
                for (int i = 1; i < length; i++) {
                    StackTraceElement item = throwable.getStackTrace()[i];
                    String classname = item.getClassName();
                    if (classname.startsWith("com.yenlien.traceyun")) {
                        exceptionInfo = "class=" + item.getClassName() + ",method=" + item.getMethodName() + ",file="
                                + item.getFileName() + ",line=" + item.getLineNumber();
                        loger.error("error trace ,exception:{}", exceptionInfo);
                        break;
                    }
                }

            }
        }
    }
}
